#include <alloca.h>
#include <time.h>
#include <string.h>
#include <unistd.h>

#include <glib.h>
#include <libxml/tree.h>
#include <xmlsec/xmldsig.h>
#include <xmlsec/crypto.h>

#include "mcx.h"

xmlDocPtr mcx_authority(xmlNodePtr keyinfo, char *currency, char *amount) {
  xmlDocPtr authority;
  xmlNodePtr ar, e;
  xmlNsPtr dosh;
  char *tmp;
  
  authority = xmlNewDoc("1.0");
  ar = xmlNewDocNode(authority, NULL, "authority", NULL);
  dosh = xmlNewNs(ar, "urn:dosh", "dosh");
  xmlSetNs(ar, dosh);
  xmlDocSetRootElement(authority, ar);

  /* recipient */
  e = xmlNewDocNode(authority, dosh, "recipient", NULL);
  xmlAddChild(e, xmlDocCopyNode(keyinfo, authority, 1));
  xmlAddChild(ar, e);
  /* amount */
  e = xmlNewDocNode(authority, dosh, "amount", amount);
  xmlSetProp(e, "currency", currency);
  xmlAddChild(ar, e);
  /* validityInterval */
  e = xmlNewDocNode(authority, dosh, "validityInterval", NULL);
  tmp = mcx_time(time(NULL));
  tmp[strlen(tmp) - 6] = 'Z';
  tmp[strlen(tmp) - 5] = 0;
  xmlAddChild(e, xmlNewDocNode(authority, dosh, "notBefore", tmp));
  tmp = mcx_time(time(NULL) + 7 * 86400);
  tmp[strlen(tmp) - 6] = 'Z';
  tmp[strlen(tmp) - 5] = 0;
  xmlAddChild(e, xmlNewDocNode(authority, dosh, "notAfter", tmp));
  xmlAddChild(ar, e);
  return authority;
}

char *mcx_time(time_t time) {
  char *tf = alloca(50);
  
  memset(tf, 0, 30);
  strftime(tf, 29, "%Y-%m-%dT%H:%M:%S%z", gmtime(&time));
  memmove(tf + strlen(tf) - 1, tf + strlen(tf) - 2, 3);
  tf[strlen(tf) - 3] = ':';
  return g_strdup(tf);
}

#if 0
void printReferences(xmlSecDSigCtxPtr ds) {
  int i;
  xmlSecDSigReferenceCtxPtr r;
  xmlSecBufferPtr buf;

  for(i = 0; i < xmlSecPtrListGetSize(&ds->signedInfoReferences); i++) {
    r = xmlSecPtrListGetItem(&ds->signedInfoReferences, i);
    buf = xmlSecDSigReferenceCtxGetPreDigestBuffer(r);
    fprintf(stderr, "signedinfo reference %d (%d):\n", i, buf->size);
    write(2, buf->data, buf->size);
    fprintf(stderr, "\n end signedinfo reference\n");
  }
}
#endif

xmlNodePtr mcx_sign(xmlNodePtr cleartext, xmlSecKeyPtr key) {
  xmlDocPtr sigdoc;
  xmlNodePtr signode;
  xmlSecDSigCtxPtr ds;

  sigdoc = xmlParseFile(DATADIR "/signature-template.xml");
  if(!sigdoc)
    return NULL;
  signode = xmlDocCopyNode(xmlDocGetRootElement(sigdoc), cleartext->doc, 1);
  if(!signode)
    return NULL;
  // interestingly, if you attach the signature to the document after
  // signing, as opposed to before, verifying the signature fails.
  xmlAddChild(cleartext, signode);
  ds = xmlSecDSigCtxCreate(NULL);
  if(!ds)
    return NULL;
  ds->flags |= XMLSEC_DSIG_FLAGS_STORE_SIGNEDINFO_REFERENCES | XMLSEC_DSIG_FLAGS_STORE_SIGNATURE;
  ds->signKey = key;
  if(xmlSecDSigCtxSign(ds, signode))
    return NULL;		/* FIXME: memory leak */
  // printReferences(ds);
  return signode;
}

xmlSecKeyPtr mcx_loadkey(char *path) {
  xmlSecKeyPtr key;
  key = xmlSecCryptoAppKeyLoad(path, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
  return key;
}
